7.2 Eine eigene Klasse
 
Auf dem Startformular befindet sich ein Button, der den Namen »butEis« trägt und die Beschriftung »Eismaschine«. Mit seiner Hilfe soll Speiseeis erzeugt werden (siehe Abbildung 7.1).
Abbildung 7.1
Das neue Formular
Im Projektmappen-Explorer kann über das Kontextmenü Hinzufügen . Klasse hinzufügen (oder das Menü Projekt . Klasse hinzufügen) eine neue Klasse eingefügt werden. Sie wird unter dem Namen »clsSpeiseeis« gespeichert und steht daraufhin im Projektmappen-Explorer. Sie besteht bereits aus den beiden Zeilen
Public Class clsSpeiseeis
End Class
Abbildung 7.2
Die neue Klasse
Das Schlüsselwort Public ist verständlich, da schließlich von einer anderen Klasse, das heißt von unserem Formular, diese Klasse aufgerufen werden soll.
Abbildung 7.3
Der Code
7.2.1 Members
 
Die einfachste Möglichkeit, eine Eigenschaft festzulegen, besteht darin, in die Klasse die Zeile
Public Kugelpreis as Decimal
einzufügen. Nun kann im Formular schon damit gearbeitet werden. Zuerst wird es instanziert, das heißt deklariert. Da beim Laden des Formulars die Eigenschaft festgelegt wird und da möglicherweise das Objekt mehreren Steuerelementen zur Verfügung stehen muss, wird es global, das heißt für das gesamte Formular deklariert. Also befindet sich am Anfang des Codes hinter
Public Class frmStart
Inherits System.Windows.Forms.Form
die Zeile:
Dim Speiseeis As New clsSpeiseeis()
Ganz genau könnte man sogar schreiben:
Private Dim Speiseeis As New clsSpeiseeis()
oder natürlich:
Private Dim Speiseeis As clsSpeiseeis()
Speiseeis = New clsSpeiseeis()
Abbildung 7.4
Das neue Objekt erscheint in der Liste.
Und nun kann im Ereignis Load, also in
Private Sub Form1_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
End Load
eingefügt werden:
Speiseeis.Kugelpreis = 1.2
Abbildung 7.5
Das Objekt erhält eine Methode.
Nach der Eingabe des Punkts hinter »Speiseeis« erscheint nicht nur die Eigenschaft »Preis«, sondern auch die Methode »GetType«. Sie steht in jeder Klasse zur Verfügung. Achten Sie darauf, dass die Dezimalzahl im Code mit einem Punkt und nicht mit einem Komma geschrieben wird!
Die Ausgabe erfolgt hinter dem Button »butEis«:
Private Sub butEis_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
MessageBox.Show("Das Eis kostet: " & _
Speiseeis.Kugelpreis)
End Sub
Bis hierher haben wir eine einfache Eigenschaft »Preis« unseres Objekts »Speiseeis« erstellt. Es muss Public deklariert werden, damit es von »außen« verwendet werden kann. Diese Eigenschaft kann nun gesetzt und abgefragt werden.
7.2.2 Eigenschaften
 
Doch solche Eigenschaften können auch komplexer aufgebaut werden. In unserer Klasse wird eine zweite Variable deklariert, die allerdings nur innerhalb der Klasse verwendet wird:
Private m_Sorte As String
Werden die Sorten nur gelesen, so wird eine Eigenschaft installiert:
ReadOnly Property Sorte() As String
Get
Return m_Sorte
End Get
End Property
Nun muss die Eissorte noch gefüllt werden. Dies übernimmt eine Methode, die sich hinter End Property befindet:
Public Sub EisFüllen()
m_Sorte = "Schokolade"
End Sub
Das bedeutet, dass zuerst die Methode aufgerufen werden muss und danach die Eigenschaft abgefragt werden kann:
Private Sub butEis_Click(ByVal sender As System.Object, _
ByVal e As System.EventArgs)
Speiseeis.EisFüllen
MessageBox.Show("Die heutige Eisgeschmacksrichtung: " _
& Speiseeis.Sorte)
End Sub
Abbildung 7.6
Über eine Methode wird eine Eigenschaft festgelegt.
Eigenschaften müssen jedoch nicht schreibgeschützt sein, man kann sie auch änderbar zur Verfügung stellen. Angenommen, der gesamte Preis darf 10 Euro nicht überschreiten. Dann kann der Endpreis gesetzt und abgefragt werden:
Private m_Endpreis As Decimal = 1.2
Property Endpreis() As Decimal
Get
Return m_Endpreis
End Get
Set(ByVal Value As Decimal)
If Value >=10 Then
m_Endpreis = 10
Else
m_Endpreis = Value
End If
End Set
End Property
Die private Variable m_Endpreis ist nötig, da der Endpreis sowohl gesetzt (Set) als auch gelesen wird (Get). Der Wert, den der Benutzer eingibt, wird über die Variable Value übergeben und kann dann weiterverarbeitet werden. Nun kann eine Eigenschaft natürlich Werte einer anderen Eigenschaft weiterverarbeiten. Wer am Anfang Schwierigkeiten mit den beiden Schlüsselwörtern Get und Set hat, der muss lediglich auf die Klammer achten, in der ein Parameter übergeben wird: Set erhält einen Wert, das heißt bei Set wird die Eigenschaft gesetzt, Get erhält keinen, gibt folglich einen Wert zurück, also ist Get für das Lesen der Eigenschaft verantwortlich.
Im folgenden Beispiel wird die Kugelzahl festgelegt und der Endpreis definiert. Daraus berechnet sich der Endpreis neu. Es werden deklariert:
Public Kugelzahl As Byte
Private m_Endpreis as Decimal = 1.2
Property Endpreis() As Decimal
Get
If Kugelzahl > 10 Then
Kugelzahl = 10
End If
If Kugelpreis * Kugelzahl < 12 Then
m_Endpreis = Kugelpreis * Kugelzahl
Else
m_Endpreis = 12
End If
Return m_Endpreis
End Get
Set(ByVal Value As Decimal)
m_Endpreis = Value
End Set
End Property
...
End Class
Wird nun die Kugelzahl auf 20 festgelegt und der Kugelpreis auf 1 Euro, so liefert
MessageBox.Show("Das Speiseeis kostet: " & _
Speiseeis.Endpreis.ToString("C")
10, weil die Kugelzahl auf 10 reduziert wird - und damit auch die Eigenschaft Endpreis. Bei 5 Kugeln und einem Preis von 1,50 Euro wird korrekt 7,50 Euro für das Speiseeis berechnet.
Abbildung 7.7
Kugeln à 1,00 Euro
7.2.3 Methoden
 
Werden Parameter übergeben, die dann weiterverarbeitet werden, so sollten Sie eine Funktion verwenden:
Function Zuschlag(ByVal Größe As String) As Decimal
Dim decZuschlag As Decimal
Select Case Größe
Case "klein"
decZuschlag = 0
Case "mittel"
decZuschlag = 0.5
Case "groß"
decZuschlag = 1
Case Else
decZuschlag = 0
End Select
Return decZuschlag
End Function
Diese Funktion wird beispielsweise aufgerufen über:
MessageBox.Show("Zuschlag: " & _
Speiseeis.Zuschlag("mittel").ToString("C")
Abbildung 7.8
Eine Methode erhält einen Wert, verarbeitet ihn und gibt einen Wert zurück.
Wird eine Prozedur eingebaut, so fungiert sie als Methode. In der Klasse befindet sich:
Sub Aufforderung()
MessageBox.Show("Bitte zahlen Sie das Eis!")
End Sub
Diese Prozedur wird aktiviert durch:
Speiseeis.Aufforderung()
Sicherlich ist Ihnen in VB.NET schon aufgefallen, dass keine klare Trennung zwischen den unterschiedlichen Methoden vorliegt. Mit dem Erzeugen eigener Methoden wird deutlich, warum dies so ist:
Eine Methode »tut« in der Regel etwas (so wie im obigen Beispiel die Prozedur »Aufforderung«). Die Methode könnte aber auch einen Wert zurückgeben. Beispielsweise so:
Sub Aufforderung() As String
Return "Bitte zahlen Sie das Eis!"
End Sub
Die Prozedur könnte auch einen Wert erhalten:
Sub Aufforderung(Text As String) As String
Return "Bitte zahlen Sie das " & Text
End Sub
Damit wird die Prozedur zu einer Funktion: Sie erhält einen Wert und gibt einen zurück. Analog könnte man auch schreiben:
Function Aufforderung(Text As String) As String
Return "Bitte zahlen Sie das " & Text
End Function
In der Praxis gibt es keinen Unterschied zwischen einer Funktion und einer Prozedur: Beide können entweder keinen, einen Parameter oder mehrere Parameter entgegennehmen. Beide können etwas ausführen und können (müssen aber nicht) einen Wert zurückgeben. Ich persönlich versuche, zwischen Prozeduren und Funktionen zu trennen (damit ich später noch weiß, was ich getan habe). Bei mir geben Funktionen immer einen Wert zurück, Prozeduren nie. Aber das ist auch schon der einzige Unterschied. Man könnte es auch anders machen.
Wenn Sie in VB.NET eine Methode sehen und nicht wissen, wie sie korrekt verarbeitet wird, dann geben Sie nach der Methode eine Klammer ein: Denn jede Methode verlangt eine Klammer. Wird nach einem Parameter gefragt, dann muss er eingegeben werden. Gibt die Methode einen Wert zurück, so ist der Typ hinter der schließenden Klammer mit einem As gekennzeichnet.
7.2.4 Konstruktoren
 
Zu Beginn des Kapitels haben wir den Member »Kugelpreis« festgelegt, der als Eigenschaft fungiert. Angenommen, eine Klasse wird instanziert und eine Eigenschaft oder ein Member soll nun schon einen Vorgabewert besitzen. Dann kann dies mit dem Schlüsselwort New in einer Klasse festgelegt werden:
Public Sub New()
Kugelpreis = 1
m_Sorte = "Erdbeere"
End Sub
Das Schlüsselwort New ist bekannt: Wird eine Klasse das erste Mal verwendet, dann wird sie mit New aufgerufen. Manche Klassen besitzen allerdings verschiedene Varianten, zwischen denen der Benutzer mit der Pfeiltaste oder der Maus wechseln kann. Soll nicht nur eine Prozedur »New« zur Verfügung gestellt werden, sondern mehrere, dann wird es wie folgt realisiert:
Public Sub New(ByVal Tagessorte As String)
Kugelpreis = 1
m_Sorte = Tagessorte
End Sub
7.2.5 Überladen
 
Problemlos können mehrere Prozeduren koexistieren, wenn sie unterschiedliche Parameter besitzen. Startet der Benutzer nun seine Klasse mit
Private Dim Speiseeis As New clsSpeiseeis()
oder mit:
Private Dim Speiseeis As clsSpeiseeis()
Speiseeis = New clsSpeiseeis()
so wird er nach Eingabe der öffnenden Klammer gefragt, welche der beiden Varianten er bevorzugt. Einen solchen Konstruktor nennt man »überladen«.
Abbildung 7.9
Überladene Konstruktoren sind bekannt, beispielsweise vn MessageBox.Show.
Ebenso können Methoden überladen werden. Neben einer Funktion, welche die Mehrwertsteuer mit 16 % berechnet, soll eine zweite Funktion implementiert werden, die eine variable Mehrwertsteuer zulässt:
Public Function MWSt() As Decimal
Return Preis / 1.16 * 0.16
End Function
Die zweite Funktion trägt den gleichen Namen, allerdings verlangt sie einen Parameter:
Public Function MWSt(MWStSatz As Decimal) As Decimal
Return Preis / (1 + MWStSatz) * MWStSatz
End Function
Damit wird die Methode auf zweierlei Arten angezeigt, wenn sie an anderer Stelle verwendet wird.
Abbildung 7.10
Eine überladene Methode
7.2.6 Vererbung
 
Interessant wird das Klassenkonzept einer objektorientierten Programmiersprache, wenn Vererbung ins Spiel kommt. Das bedeutet, dass eine zweite Klasse erstellt werden kann: Hinzufügen . Klasse. Sie erhält den Namen »clsEisbecher«. Damit sie die Methoden und Eigenschaften der ersten Klasse vererbt bekommt, wird nach
Public Class clsEisbecher
die folgende Zeile eingefügt:
Inherits clsSpeiseeis
Sämtliche Methoden und Eigenschaften stehen nun zur Verfügung, wenn wie folgt deklariert wird:
Dim eisbecher As clsEisbecher
eisbecher = New clsEisbecher()
With eisbecher
Messagebox.Show("Die Kugel kostet immer noch: " & _
.Preis.ToString("C"))
End With
Abbildung 7.11
Die Eigenschaft »Kugelpreis« wurde vererbt.
Natürlich können in der Klasse clsEisbecher eigene Eigenschaften und Methoden implantiert werden:
Private m_Alkohol As Boolean
Property Alkohol() As Boolean
Get
Return m_Alkohol
End Get
Set(ByVal Value As Boolean)
m_Alkohol = Value
End Set
End Property
Diese Eigenschaft steht nur der Klasse clsEisbecher zur Verfügung, nicht der Klasse clsSpeiseeis.
7.2.7 Überschreiben
 
Und was macht man, wenn man eine Methode oder Eigenschaft gleichen Namens in zwei Klassen benötigt, die aber jeweils unterschiedlich arbeiten soll? Angenommen, die Klasse clsSpeiseeis besitzt folgende Funktion:
Function Aufforderung2(Text As String) As String
Return "Bitte zahlen Sie das " & Text
End Function
Wird diese Klasse nach clsEisbecher vererbt, so darf dort nicht stehen:
Function Aufforderung2(Text As String) As String
Return "Bitte zahlen Sie sofort das " & Text
End Function
Abbildung 7.12
So geht das nicht!
Dies würde zu einem Fehler führen, der angezeigt wird. Damit dies dennoch ermöglicht wird, muss die Funktion in der Klasse clsSpeiseeis als überschreibbar gekennzeichnet werden. Das Schlüsselwort lautet Overridable:
Overridable Function Aufforderung2(Text As String) _
As String
Return "Bitte zahlen Sie das " & Text
End Function
Auf der anderen Seite - in der Klasse clsEisbecher - wird das Schlüsselwort Overrides verwendet:
Overrides Function Aufforderung(Text As String) As String
Return "Bitte zahlen Sie sofort das " & Text
End Function
Und damit sind in zwei voneinander abgeleiteten Klassen zwei Methoden implementiert, die unterschiedliche Dinge tun.
Abbildung 7.13
Aufforderung2a
Abbildung 7.14
Aufforderung2b
7.2.8 Polymorphismus
 
Der letzte und vielleicht entsetzlichste Begriff aus der Welt der Objektorientierung lautet »Polymorphismus«. Damit ist folgendes Konzept gemeint:
Eine Funktion ZeigeSorte verwendet die Klasse clsSpeiseeis:
Function ZeigeSorte(ByVal Eis As clsSpeiseeis) As String
Return Eis.Sorte
End Function
Diese Funktion erhält einen Wert vom Typ clsSpeiseeis, der an die Variable Eis übergeben wird. Diese wird weiterverarbeitet. Sie wird beispielsweise folgendermaßen aufgerufen:
Dim speiseeis As clsSpeiseeis
speiseeis = New clsSpeiseeis()
Messagebox.Show(ZeigeSorte(speiseeis))
Erstaunlicherweise kann auch ein anderer Typ verwendet werden:
Dim eisbecher As clsEisbecher
eisbecher = New Eisbecher()
Messagebox.Show(ZeigeSorte(eisbecher))
Nun wird die Klasse eisbecher vom Typ clsEisbecher übergeben. Da sie von der Klasse clsSpeiseeis abgeleitet ist, funktioniert dies.
|